﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace Program
{
    internal class ClientSocket
    {
        private static int CLIENT_BEING_ID = 1;
        public int clientId;
        public Socket socket;
        //用于处理分包时 缓存的字节数组和长度
        private byte[] cacheBytes = new byte[1024 * 1024];
        private int cacheLen;

        public ClientSocket(Socket socket)
        {
            this.clientId = CLIENT_BEING_ID;
            this.socket = socket;
            CLIENT_BEING_ID++;
        }

        public int ClientId => this.clientId;

        //连接状态处理
        public bool IsConnected => this.socket.Connected;

        public void Close()
        {
            if (socket == null)
                return;
            Console.WriteLine("[ClientSocket][Close] Clinet:{0}!", this.clientId);
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
            socket = null;
        }

        public void Send(BaseMsg msg)
        {
            if (IsConnected == false)
            {
                Program.socket.AddDelSocket(this);
                return;
            }
            try
            {
                socket.Send(msg.Writing());
            }
            catch (Exception e)
            {
                Program.socket.AddDelSocket(this);
                Console.WriteLine("[ClientSocket][Send] Fail(Id:{0} Error:{1})", clientId, e.Message);
            }
        }
        public void Receive()
        {
            if (IsConnected == false)
            {
                Program.socket.AddDelSocket(this);
                return;
            }
            if (socket.Available == 0)
                return;
            byte[] recvBytes = new byte[1024];
            try
            {
                int recvLen = socket.Receive(recvBytes);
                HandleReceiveMsg(recvBytes, recvLen);
            }
            catch (Exception e)
            {
                Program.socket.AddDelSocket(this);
                Console.WriteLine("[ClientSocket][Receive] Fail(Id:{0} Error:{1})", clientId, e.Message);
            }
        }

        /// <summary>
        /// 处理接受消息
        /// </summary>
        /// <param name="receiveBytes"></param>
        /// <param name="recvLen"></param>
        /// 正常情况|黏包|分包|分包+黏报 几种情况的全场景处理.

        private void HandleReceiveMsg(byte[] receiveBytes, int recvLen)
        {
            int msgID = 0;
            int msgLen = 0;
            int nowIndex = 0;

            //黏包分包处理
            receiveBytes.CopyTo(cacheBytes, cacheLen);
            cacheLen += recvLen;

            while (true)
            {
                //根据-1判断是否解析了包头
                msgLen = -1;
                if (cacheLen - nowIndex >= 8)
                {
                    //解析ID
                    msgID = BitConverter.ToInt32(cacheBytes, 0);
                    nowIndex += 4;
                    //解析长度
                    msgLen = BitConverter.ToInt32(cacheBytes, nowIndex);
                    nowIndex += 4;
                }
                //排队消息头,剩余长度>=消息长度,表示至少有一个可解析的消息体
                if (cacheLen - nowIndex >= msgLen && msgLen != -1)
                {
                    //解析消息体
                    BaseMsg baseMsg = null;
                    switch (msgID)
                    {
                        case 1001:
                            {
                                baseMsg = new RoleMsg();
                                baseMsg.Reading(cacheBytes, nowIndex);
                                break;
                            }
                        case 1003:
                            {
                                baseMsg = new QuitMsg();
                                break;
                            }
                    }
                    if (baseMsg != null)
                        ThreadPool.QueueUserWorkItem(MsgHandle, baseMsg);
                    nowIndex += msgLen;
                    if (nowIndex == cacheLen)
                    {
                        cacheLen = 0; 
                        break;
                    }
                }
                else
                {
                    //如果进行了 id和长度的解析 但是没有成功解析消息体 需要减去nowIndex移动的位置
                    if (msgLen != -1)
                        nowIndex -= 8;
                    //将没有解析出来的数据重新移到缓存从0开始的位置 下次继续解析
                    Array.Copy(cacheBytes, nowIndex, cacheBytes, 0, cacheLen - nowIndex);
                    cacheLen = cacheLen - nowIndex;
                    break;
                }
            }
        }

        private void MsgHandle(object obj)
        {
            if (IsConnected == false)
                return;
            try
            {
                BaseMsg msg = obj as BaseMsg;
                if (msg is RoleMsg)
                {
                    Console.WriteLine("接收到客户端{0}消息:", clientId);
                    (msg as RoleMsg).Print();
                }
                else if(msg is QuitMsg)
                {
                    Program.socket.AddDelSocket(this);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("[ClientSocket][MsgHandle] Fail(Id:{0} Error:{1})", clientId, e.Message);
            }
        }
    }
}
